package SecureChat;
import java.io.*;
import java.net.*;

public class ChatLib{
    private Socket s;
    private String id;
    private byte[] key;

    public ChatLib(Socket s_, String id_, byte[] key_)
    {
		s = s_;
		id = id_;
		key = key_;
		Thread send = new Thread(new WriteThread(s, id, key));
		
		Thread receive = new Thread(new ListenThread(s, id, key));

    }

}

class WriteThread implements Runnable {
	 private Socket s;
	 private String id;
	 private byte[] key;    
	 private byte[] iv;
	 public WriteThread(Socket s_, String id_, byte[] key_)
	 {
		s = s_;
		id = id_;
		key = key_;
 
		Thread t = new Thread(this);   
		t.start();
	 }
	 public void run(){
	 	OutputStream sOut;
	 	String input;
	 	BufferedReader br = new BufferedReader(new InputStreamReader(System.in));
	 	byte[] send_buffer;
	 	
	 	try{
	 		sOut = s.getOutputStream();
	 	}
	 	catch(Exception e)
	 	{
	 		System.out.println("Output stream creation Error: " + e);
	 		return;
	 	}
		while(!s.isClosed())
		{
			try{
				input = br.readLine();
			}
			catch(Exception e)
			{
				System.out.println("Input reading Error: " + e);
				return;
			}
		
			if(input.length() > 0)
			{
				if(input.equals("/quit"))
				{
					//Don't encrypt the /quit because that would allow a known-plaintext
					try{
						sOut.write(input.getBytes());
					}
					catch(Exception e){
						System.out.println("/quit Output Error: " + e);
					}
					try{
						System.out.println("System: Closing connection.");
						s.close();
					}
					catch(Exception e){
						System.out.println("Close Error: " + e);
						return;
					}
					System.exit(0);
				}
			
				input = id + "> " + input;
				
				iv = Utilities.GenIV();
				try{
					input = Utilities.Encrypt(key, iv, input);
				}
				
				catch(Exception e){
					System.out.println("Encryption error: " + e);
					System.exit(1);
 				}
				
				send_buffer = new byte[iv.length + input.length()];
				System.arraycopy(iv,0,send_buffer,0,iv.length);
				System.arraycopy(input.getBytes(),0,send_buffer,iv.length,input.length());
				
				try{
					sOut.write(send_buffer);
				}
	
				catch(Exception e){
						System.out.println("Write Error: " + e);
						return;
				}
				input = "";
			}//if(input.length > 0)
		}//while(!s.isClosed())
	 }
}

class ListenThread implements Runnable {
	 private Socket s;
	 private String id;
	 private byte[] key;    
	 private InputStream sIn;
	 public ListenThread(Socket s_, String id_, byte[] key_)
	 {
		s = s_;
		id = id_;
		key = key_;

		Thread t = new Thread(this);
		t.start();   
	 }
	 
	 public void run(){
		byte[] readBuffer;
		byte[] readBuffer_split;
		byte[] iv;
	 	try{
			sIn = s.getInputStream();
		}
		catch(Exception e){
			System.out.println("Inputstream Error: " + e);
			return;
		}

		while(!s.isClosed())
		{
			try{
					if(sIn.available() > 0)
					{
					 readBuffer = new byte[sIn.available()];
					 iv = new byte[16];

					 sIn.read(readBuffer);
					 String temp = new String(readBuffer);
					 
					 if(temp.equals("/quit"))
					 {
					 	System.out.println("System: Client closing connection. Terminating.");
					 	System.exit(0);
					 }
					 
					 readBuffer_split = new byte[readBuffer.length - iv.length];
					 System.arraycopy(readBuffer,0,iv,0,iv.length);
					 System.arraycopy(readBuffer,iv.length,readBuffer_split,0,readBuffer.length - iv.length);
					 
					 try{
					 	temp = Utilities.Decrypt(key, iv, new String(readBuffer_split));
					 }
					 catch(Exception e){
					 	System.out.println("Decryption Error: " + e);
					 }
					 
					 System.out.println(temp);
					}
			}
			catch(Exception e){
						System.out.println("Error while listening on socket: " + e);
						System.exit(2);
			}
		}//while(!s.isClosed())	
	 }
}


